#pragma once
#include <iostream>
#include <memory.h>
#include <exception>
using namespace std;

typedef unsigned int uint;

template <class T>
class MyMatrix
{
    uint _col;
    uint _row;
    T *_mat;
    uint *_pcnt; //Apply Copy-On-Write
public:
    // Constructor    
    // Empty Constructor
    MyMatrix();
    // Zero Square Matrix Constructor
    MyMatrix(const uint squareRowCol);
    // Zero Matrix Constructor
    MyMatrix(const uint row, const uint col);
    // Buffer-Matrix Constructor
    template <uint Size>
    MyMatrix(const T (&buffer)[Size], const uint row, const uint col);
    // Buffer-Matrix Constructor - Unsafe
    MyMatrix(const T buffer[], const uint row, const uint col);
    // Copy Constructor
    MyMatrix(const MyMatrix<T> &m);

    // Destructor
    ~MyMatrix();

    // CheckTools    
    bool isEmpty() const;
    bool checkSameShape(const MyMatrix<T> &m) const;
    bool checkMultiply(const MyMatrix<T> &m) const;
    bool is(MyMatrix<T> m) const;
    bool equals(MyMatrix<T> m) const;

    // Math
    MyMatrix<T> fill(const T &t);
    // Add
    MyMatrix<T> operator+(const MyMatrix<T> &m) const;
    MyMatrix<T> operator+(const T &t) const;
    MyMatrix<T>&operator+=(const MyMatrix<T> &m);

    // Subtraction
    MyMatrix<T> operator-(const MyMatrix<T> &m) const;
    MyMatrix<T> operator-(const T &t) const;
    MyMatrix<T>&operator-=(const MyMatrix<T> &m);

    // Multiply
    MyMatrix<T> operator*(const MyMatrix<T> &m) const;
    MyMatrix<T> operator*(const T &t) const;
    MyMatrix<T>&operator*=(const MyMatrix<T> &m);

    // Divide
    MyMatrix<T> operator/(const T &t) const;

    // Assign
    MyMatrix<T>& operator=(const MyMatrix<T> &m);

    // Minus &Positive
    // 负号
    // the former 'const' prevents "-m = MyMatrix(...)" from happening
    // the latter 'const' makes -(-m) can be calculated properly
    const MyMatrix<T> operator-() const;

    // 正号
    // the former 'const' prevents "+m = MyMatrix(...)" from happening
    // the latter 'const' makes +(+m) can be calculated properly
    const MyMatrix<T> operator+() const;
    MyMatrix<T> transpose();

    // Basic
    MyMatrix<T> zeros();
    MyMatrix<T> zeros(const uint squareRowCol);
    MyMatrix<T> zeros(const uint row, const uint col);
    MyMatrix<T> ones();
    MyMatrix<T> ones(const uint squareRowCol);
    MyMatrix<T> ones(const uint row, const uint col);

    // Subscript
private:
    T *operator[](const uint row);

public:
    const T *operator[](const uint row) const;
    T &operator()(const int row, const int col);
    const T &operator()(const int row, const int col) const;

#pragma region Friends
#pragma region Compare
    friend bool operator==(MyMatrix<T>& m1, MyMatrix<T>& m2)
    {
        //if (m1.equals(m2) != m2.equals(m1)) throw - 1;
        return m1.equals(m2);
    }

    friend bool operator!=(MyMatrix<T>& m1, MyMatrix<T>& m2)
    {
        return !(m1 == m2);
    }
#pragma endregion

#pragma region Math
    friend MyMatrix<T> operator+(const T& t, const MyMatrix<T>& m)
    {
        MyMatrix<T> res(m._mat, m._row, m._col);

        for (uint r = 0; r < res._row; ++r)
        {
            for (uint c = 0; c < res._col; ++c)
            {
                res[r][c] = t + res[r][c];
            }
        }

        return res;
    }

    friend MyMatrix<T> operator-(const T& t, const MyMatrix<T>& m) {
        MyMatrix<T> res(m._mat, m._row, m._col);

        for (uint r = 0; r < res._row; ++r)
        {
            for (uint c = 0; c < res._col; ++c)
            {
                res[r][c] = t - res[r][c];
            }
        }

        return res;
    }

    friend MyMatrix<T> operator*(const T& t, const MyMatrix<T>& m) {
        {
            MyMatrix<T> res(m._mat, m._row, m._col);

            for (uint r = 0; r < res._row; ++r)
            {
                for (uint c = 0; c < res._col; ++c)
                {
                    res[r][c] = t * res[r][c];
                }
            }

            return res;
        }
    }

    friend MyMatrix<T> operator/(const T& t, const MyMatrix<T>& m) {
        MyMatrix<T> res(m._mat, m._row, m._col);

        for (uint r = 0; r < res._row; ++r)
        {
            for (uint c = 0; c < res._col; ++c)
            {
                res[r][c] = t / res[r][c];
            }
        }

        return res;
    }
#pragma endregion

    friend ostream& operator<<(ostream& os, const MyMatrix<T>& m)
    {
        if (m.isEmpty())
        {
            os << "<Empty Matrix>" << endl;
            return os;
        }

        os << '[';

        os << '[' << m[0][0];
        for (uint j = 1; j < m._col; ++j)
        {
            os << " \t" << m[0][j];
        }
        os << ']';

        for (uint i = 1; i < m._row; ++i)
        {
            os << endl;
            os << " [" << m[i][0];
            for (uint j = 1; j < m._col; ++j)
            {
                os << " \t" << m[i][j];
            }
            os << ']';
        }
        os << ']' << endl;
        return os;
    }
#pragma endregion
};
//(?<=[a-zA-Z &<>=!~\+\-\*\/]*\(.*\):?.*) \{
